/**
 *     This file is part of cesm-ws.
 *
 *     cesm-ws, Web Service for Community Earth System Model
 *
 *     Copyright (c) 2010, Purdue University and/or affilicates or thrid-party contributors as
 *     indicated by the @author tags or express copyright attribution statements
 *     applied by the authors. All third-party contributions are distributed under
 *     license by Purdue University.
 *
 *     cesm-ws is free software: you can redistribute it and/or modify
 *     it under the terms of the GNU General Public License as published by
 *     the Free Software Foundation, either version 3 of the License, or
 *     (at your option) any later version.
 *
 *     This program is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *     GNU General Public License for more details.
 *
 *     You should have received a copy of the GNU Lesser General Public License
 *     along with this distribution.  If not, see <http://www.gnu.org/licenses/>.
 */

package edu.purdue.rcac.cesm.ws.soap;

/**
 * Implementation of interface CCSM4SOAPInterface
 * 
 * @author Han Zhang
 **/
import java.io.File;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

import javax.activation.DataHandler;
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
import javax.jws.soap.SOAPBinding.ParameterStyle;
import javax.jws.soap.SOAPBinding.Style;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.xml.bind.annotation.XmlMimeType;

import org.apache.log4j.Logger;

import edu.purdue.rcac.cesm.Constants.EnvFile;
import edu.purdue.rcac.cesm.define.ComponentSet;
import edu.purdue.rcac.cesm.define.DirectoryAlias;
import edu.purdue.rcac.cesm.define.Machine;
import edu.purdue.rcac.cesm.define.Resolution;
import edu.purdue.rcac.cesm.facade.ServiceFacade;
import edu.purdue.rcac.cesm.facade.ServiceFacadeLocal;
import edu.purdue.rcac.cesm.facade.bean.AllCaseInfoBean;
import edu.purdue.rcac.cesm.facade.bean.AllCaseNameBean;
import edu.purdue.rcac.cesm.facade.bean.AllJobInfoBean;
import edu.purdue.rcac.cesm.facade.bean.CaseConfigFileBean;
import edu.purdue.rcac.cesm.facade.bean.CaseConfigInfoBean;
import edu.purdue.rcac.cesm.facade.bean.CaseRuntimeInfoBean;
import edu.purdue.rcac.cesm.facade.bean.ConfigVarBean;
import edu.purdue.rcac.cesm.facade.bean.ConfigVarListBean;
import edu.purdue.rcac.cesm.facade.bean.CreateCaseBean;
import edu.purdue.rcac.cesm.facade.bean.DownloadBean;
import edu.purdue.rcac.cesm.facade.bean.FileBean;
import edu.purdue.rcac.cesm.facade.bean.KeyValueEntry;
import edu.purdue.rcac.cesm.util.StringUtil;
import edu.purdue.rcac.cesm.ws.restful.CESMRestfulRuntimeException;
import edu.purdue.rcac.service.dispatch.ServiceDispatcherManagement.AuthDatabase;

@WebService(serviceName = "CESMServices", name = "CESMSOAP", targetNamespace = "http://soap.ws.cesm.rcac.purdue.edu/")
@SOAPBinding(style = Style.DOCUMENT, parameterStyle = ParameterStyle.WRAPPED)
// @BindingType(value = "http://schemas.xmlsoap.org/wsdl/soap/http?mtom=true")
public class CESMSOAPImpl implements CESMSOAPInterface {

	protected static final Logger log = Logger.getLogger(CESMSOAPImpl.class
			.getName());

	private ServiceFacadeLocal serviceFacade;

	/**
	 * Constructor
	 */
	public CESMSOAPImpl() {
		super();
		InitialContext ctx;
		try {
			ctx = new InitialContext();
			serviceFacade = (ServiceFacadeLocal) ctx
					.lookup(ServiceFacade.LOCAL_JNDI_BINDING);
		} catch (NamingException ne) {
			log.error(ne);
			serviceFacade = null;
		}
	}

	/**
	 * @return action string formated in underscore lower case
	 */
	private String getMethodName() {
		return StringUtil.underscoreLowerCase(Thread.currentThread()
				.getStackTrace()[2].getMethodName());
	}

	/**
	 * Public Interface
	 */
	@WebMethod
	// create a new CCSM test case
	public CreateCaseBean createCase(
			@WebParam(name = "resolution") String resolution,
			@WebParam(name = "componentSet") String componentSet,
			@WebParam(name = "allocation") String allocation,
			@WebParam(name = "machine") String machine,
			@WebParam(name = "caseName") String caseName,
			@WebParam(name = "userName") String userName,
			@WebParam(name = "password") String password) throws CESMSOAPFault {
		Resolution resolutionEnum;
		ComponentSet componentSetEnum;
		try {
			log.debug("resolution: " + resolution);
			log.debug("component set: " + componentSet);
			log.debug("machine: " + machine);

			resolutionEnum = Resolution.fromString(resolution);
			componentSetEnum = ComponentSet.fromString(componentSet);
			Machine.fromString(machine.trim());
		} catch (IllegalArgumentException iae) {
			CESMSOAPFaultBean faultInfo = new CESMSOAPFaultBean();
			faultInfo.setMessage(iae.getMessage());
			throw new CESMSOAPFault("input parameter is invalid", faultInfo);
		}
		try {
			return serviceFacade.createCase(resolutionEnum, componentSetEnum,
					allocation, machine, caseName, userName, password,
					AuthDatabase.PortalDB);
		} catch (Exception e) {
			throw new CESMSOAPFault(e);
		}
	}

	@WebMethod
	@WebResult(name = "outcome")
	// cancel a case which is either in queue or running
	public String cancelCase(@WebParam(name = "userName") String userName,
			@WebParam(name = "caseName") String caseName,
			@WebParam(name = "token") String token) throws CESMSOAPFault {

		try {
			return serviceFacade.cancelCase(userName, caseName, token);
		} catch (Exception e) {
			throw new CESMSOAPFault(e);
		}
	}

	@WebMethod
	@WebResult(name = "outcome")
	// configure a case
	public String configureCase(@WebParam(name = "userName") String userName,
			@WebParam(name = "caseName") String caseName,
			@WebParam(name = "token") String token) throws CESMSOAPFault {

		try {
			return serviceFacade.configureCase(userName, caseName, token);
		} catch (Exception e) {
			throw new CESMSOAPFault(e);
		}
	}

	@WebMethod
	@WebResult(name = "outcome")
	// delete all the data related to a case from the system
	public String deleteCase(@WebParam(name = "userName") String userName,
			@WebParam(name = "caseName") String caseName,
			@WebParam(name = "token") String token) throws CESMSOAPFault {
		try {
			return serviceFacade.deleteCase(userName, caseName, token);
		} catch (Exception e) {
			throw new CESMSOAPFault(e);
		}
	}

	@WebMethod
	@WebResult(name = "outcome")
	// Build and Run a CCSM case
	public String runCase(@WebParam(name = "userName") String userName,
			@WebParam(name = "caseName") String caseName,
			@WebParam(name = "email") String email,
			@WebParam(name = "queueToRun") String queueToRun,
			@WebParam(name = "wallTime") String wallTime,
			@WebParam(name = "token") String token) throws CESMSOAPFault {
		try {
			return serviceFacade.runCase(userName, caseName, email, queueToRun,
					wallTime, token);
		} catch (Exception e) {
			throw new CESMSOAPFault(e);
		}
	}

	@WebMethod
	// show all the cases owned by a user
	public AllCaseNameBean listAllCaseName(
			@WebParam(name = "userName") String userName,
			@WebParam(name = "token") String token) throws CESMSOAPFault {

		try {
			return serviceFacade.listAllCaseName(userName, token);
		} catch (Exception e) {
			throw new CESMSOAPFault(e);
		}
	}

	@WebMethod
	@WebResult(name = "outcome")
	// query the status of a case
	public String getJobStatus(@WebParam(name = "userName") String userName,
			@WebParam(name = "caseName") String caseName,
			@WebParam(name = "token") String token) throws CESMSOAPFault {
		try {
			return serviceFacade.getJobStatus(userName, caseName, token);
		} catch (Exception e) {
			throw new CESMSOAPFault(e);
		}
	}

	@WebMethod
	@WebResult(name = "url")
	// return output file location for download (http/ftp?)
	public String getOutputLocation(
			@WebParam(name = "userName") String userName,
			@WebParam(name = "caseName") String caseName,
			@WebParam(name = "token") String token) throws CESMSOAPFault {
		try {
			return serviceFacade.getOutputLocation(userName, caseName, token);
		} catch (Exception e) {
			throw new CESMSOAPFault(e);
		}
	}

	@WebMethod
	@WebResult(name = "used-cpu-time")
	// query the CPU usage
	public String getUsedCPUTime(@WebParam(name = "userName") String userName,
			@WebParam(name = "caseName") String caseName,
			@WebParam(name = "token") String token) throws CESMSOAPFault {
		try {
			return serviceFacade.getUsedCPUTime(userName, caseName, token);
		} catch (Exception e) {
			throw new CESMSOAPFault(e);
		}
	}

	@WebMethod
	@WebResult(name = "used-wall-time")
	// query the walltime usage
	public String getUsedWallTime(@WebParam(name = "userName") String userName,
			@WebParam(name = "caseName") String caseName,
			@WebParam(name = "token") String token) throws CESMSOAPFault {
		try {
			return serviceFacade.getUsedWallTime(userName, caseName, token);
		} catch (Exception e) {
			throw new CESMSOAPFault(e);
		}
	}

	@WebMethod
	// get the current value of a configuration variable
	public ConfigVarBean queryConfigVar(
			@WebParam(name = "varName") String varName,
			@WebParam(name = "userName") String userName,
			@WebParam(name = "caseName") String caseName,
			@WebParam(name = "token") String token) throws CESMSOAPFault {

		try {
			return serviceFacade.queryConfigVar(varName, userName, caseName,
					token);
		} catch (Exception e) {
			throw new CESMSOAPFault(e);
		}
	}

	@WebMethod
	// Reset configuration
	public String resetConfig(@WebParam(name = "userName") String userName,
			@WebParam(name = "caseName") String caseName,
			@WebParam(name = "token") String token) throws CESMSOAPFault {
		try {
			return serviceFacade.resetConfig(userName, caseName, token);
		} catch (Exception e) {
			throw new CESMSOAPFault(e);
		}
	}

	@WebMethod
	@WebResult(name = "outcome")
	// set the value of a configuration variable
	public String setConfigVar(@WebParam(name = "varName") String varName,
			@WebParam(name = "varValue") String varValue,
			@WebParam(name = "userName") String userName,
			@WebParam(name = "caseName") String caseName,
			@WebParam(name = "token") String token) throws CESMSOAPFault {

		try {
			return serviceFacade.setConfigVar(varName, varValue, userName,
					caseName, token);
		} catch (Exception e) {
			throw new CESMSOAPFault(e);
		}
	}

	@WebMethod
	@WebResult(name = "token-valid")
	// return a boolean indicates if this token has been expired
	public boolean isTokenValid(@WebParam(name = "userName") String userName,
			@WebParam(name = "token") String token) throws CESMSOAPFault {
		try {
			return serviceFacade.isTokenValid(userName, token);
		} catch (Exception e) {
			throw new CESMSOAPFault(e);
		}
	}

	@WebMethod
	@WebResult(name = "pbs-err")
	// return pbs.err content
	public String getPbsErr(@WebParam(name = "userName") String userName,
			@WebParam(name = "caseName") String caseName,
			@WebParam(name = "token") String token) throws CESMSOAPFault {
		try {
			return serviceFacade.getPbsErr(userName, caseName, token);
		} catch (Exception e) {
			throw new CESMSOAPFault(e);
		}
	}

	@WebMethod
	@WebResult(name = "pbs-out")
	// return pbs.out content
	public String getPbsOut(@WebParam(name = "userName") String userName,
			@WebParam(name = "caseName") String caseName,
			@WebParam(name = "token") String token) throws CESMSOAPFault {

		try {
			return serviceFacade.getPbsOut(userName, caseName, token);
		} catch (Exception e) {
			throw new CESMSOAPFault(e);
		}
	}

	@WebMethod
	public CaseConfigInfoBean getCaseConfigInfo(
			@WebParam(name = "userName") String userName,
			@WebParam(name = "caseName") String caseName,
			@WebParam(name = "token") String token) throws CESMSOAPFault {
		try {
			return serviceFacade.getCaseConfigInfo(userName, caseName, token);
		} catch (Exception e) {
			throw new CESMSOAPFault(e);
		}
	}

	@WebMethod
	public CaseRuntimeInfoBean getCaseRuntimeInfo(
			@WebParam(name = "userName") String userName,
			@WebParam(name = "caseName") String caseName,
			@WebParam(name = "token") String token) throws CESMSOAPFault {
		try {
			return serviceFacade.getCaseRuntimeInfo(userName, caseName, token);
		} catch (Exception e) {
			throw new CESMSOAPFault(e);
		}
	}

	@WebMethod
	public AllJobInfoBean listAllJobInfo(
			@WebParam(name = "userName") String userName,
			@WebParam(name = "queueName") String queueName,
			@WebParam(name = "machine") String machine,
			@WebParam(name = "token") String token) throws CESMSOAPFault {
		try {
			return serviceFacade.listAllJobInfo(userName, queueName, machine,
					token);
		} catch (Exception e) {
			throw new CESMSOAPFault(e);
		}
	}

	@WebMethod
	@WebResult(name = "outcome")
	public String setConfigVarList(
			@WebParam(name = "userName") String userName,
			@WebParam(name = "caseName") String caseName,
			@WebParam(name = "var") List<KeyValueEntry> varList,
			@WebParam(name = "token") String token) throws CESMSOAPFault {
		List<KeyValueEntry> pairs = new ArrayList<KeyValueEntry>();
		try {
			return serviceFacade.setConfigVarList(userName, caseName, varList,
					token);
		} catch (Exception e) {
			throw new CESMSOAPFault(e);
		}
	}

	@WebMethod
	public AllCaseInfoBean listAllCaseInfo(
			@WebParam(name = "userName") String userName,
			@WebParam(name = "token") String token) throws CESMSOAPFault {
		try {
			return serviceFacade.listAllCaseInfo(userName, token);
		} catch (Exception e) {
			throw new CESMSOAPFault(e);
		}
	}

	@WebMethod
	// list all the configuration variables that can be changed
	public ConfigVarListBean getConfigVarList(
			@WebParam(name = "userName") String userName,
			@WebParam(name = "caseName") String caseName,
			@WebParam(name = "token") String token) throws CESMSOAPFault {

		try {
			return serviceFacade.getConfigVarList(userName, caseName, token);
		} catch (Exception e) {
			throw new CESMSOAPFault(e);
		}
	}

	@WebMethod
	@WebResult(name = "token")
	// return a token if user is authenticated, otherwise return ""
	public String authenticate(@WebParam(name = "userName") String userName,
			@WebParam(name = "password") String password) throws CESMSOAPFault {
		try {
			return serviceFacade.authenticate(userName, password,
					AuthDatabase.PortalDB);
		} catch (Exception e) {
			throw new CESMSOAPFault(e);
		}
	}

	@WebMethod
	@WebResult(name = "outcome")
	public String createUserHome(@WebParam(name = "userName") String userName,
			@WebParam(name = "token") String token) throws CESMSOAPFault {
		try {
			return serviceFacade.createUserHome(userName, token);
		} catch (Exception e) {
			throw new CESMSOAPFault(e);
		}
	}

	@WebMethod
	@WebResult(name = "outcome")
	@XmlMimeType("application/octet-stream")
	public String upload(
			@WebParam(name = "userName") String userName,
			@WebParam(name = "targetDir") DirectoryAlias targetDir,
			@WebParam(name = "path") String toPath,
			@XmlMimeType("application/octet-stream") @WebParam(name = "data") DataHandler data,
			@WebParam(name = "token") String token) throws CESMSOAPFault {
		try {
			InputStream is = data.getInputStream();
			byte[] buffer = new byte[2048];

			log.info("SIZE: " + is.read(buffer));
			log.info(buffer);

			return serviceFacade.upload(userName, targetDir.getAbsolutePath()
					+ File.separator + userName + File.separator + toPath,
					data, token);
		} catch (Exception e) {
			throw new CESMSOAPFault(e);
		}
	}

	/**
	 * FTP OPERATION
	 */
	@WebMethod
	public List<FileBean> list(@WebParam(name = "userName") String userName,
			@WebParam(name = "path") String path,
			@WebParam(name = "baseDir") DirectoryAlias basedir,
			@WebParam(name = "token") String token) throws CESMSOAPFault {
		try {
			return serviceFacade.list(userName, path, basedir.getAbsolutePath()
					+ userName + File.separator, token);
		} catch (Exception e) {
			throw new CESMSOAPFault(e);
		}
	}

	@WebMethod
	@WebResult(name = "outcome")
	public String mkdir(@WebParam(name = "userName") String userName,
			@WebParam(name = "path") String path,
			@WebParam(name = "baseDir") DirectoryAlias basedir,
			@WebParam(name = "token") String token) throws CESMSOAPFault {
		try {
			return serviceFacade.mkdir(userName, path,
					basedir.getAbsolutePath() + userName + File.separator,
					token);
		} catch (Exception e) {
			throw new CESMSOAPFault(e);
		}
	}

	@WebMethod
	@WebResult(name = "outcome")
	public String remove(@WebParam(name = "userName") String userName,
			@WebParam(name = "path") String path,
			@WebParam(name = "baseDir") DirectoryAlias basedir,
			@WebParam(name = "token") String token) throws CESMSOAPFault {
		try {
			return serviceFacade.remove(userName, path,
					basedir.getAbsolutePath() + userName + File.separator,
					token);
		} catch (Exception e) {
			throw new CESMSOAPFault(e);
		}
	}

	@WebMethod
	@WebResult(name = "outcome")
	public String move(@WebParam(name = "userName") String userName,
			@WebParam(name = "fromPath") String fromPath,
			@WebParam(name = "toPath") String toPath,
			@WebParam(name = "baseDir") DirectoryAlias basedir,
			@WebParam(name = "token") String token) throws CESMSOAPFault {
		try {
			return serviceFacade.move(userName, fromPath, toPath,
					basedir.getAbsolutePath() + userName + File.separator,
					token);
		} catch (Exception e) {
			throw new CESMSOAPFault(e);
		}
	}

	@WebMethod
	@WebResult(name = "outcome")
	public String rename(@WebParam(name = "userName") String userName,
			@WebParam(name = "fromPath") String fromPath,
			@WebParam(name = "toPath") String toPath,
			@WebParam(name = "baseDir") DirectoryAlias basedir,
			@WebParam(name = "token") String token) throws CESMSOAPFault {
		try {
			return serviceFacade.rename(userName, fromPath, toPath,
					basedir.getAbsolutePath() + userName + File.separator,
					token);
		} catch (Exception e) {
			throw new CESMSOAPFault(e);
		}
	}

	@WebMethod
	@XmlMimeType("application/octet-stream")
	public DownloadBean download(@WebParam(name = "userName") String userName,
			@WebParam(name = "targetDir") DirectoryAlias targetDir,
			@WebParam(name = "path") String path,
			@WebParam(name = "token") String token) throws CESMSOAPFault {
		try {
			return serviceFacade.download(userName, targetDir.getAbsolutePath()
					+ File.separator + userName + File.separator + path, token);
		} catch (Exception e) {
			throw new CESMSOAPFault(e);
		}
	}

	@WebMethod
	@WebResult(name = "outcome")
	public String publish(@WebParam(name = "userName") String userName,
			@WebParam(name = "caseName") String caseName,
			@WebParam(name = "token") String token) throws CESMSOAPFault {
		try {
			return serviceFacade.publish(userName, caseName, token);
		} catch (Exception e) {
			throw new CESMSOAPFault(e);
		}
	}

	@WebMethod
	@WebResult(name = "outcome")
	public String unpublish(@WebParam(name = "userName") String userName,
			@WebParam(name = "caseName") String caseName,
			@WebParam(name = "token") String token) throws CESMSOAPFault {
		try {
			return serviceFacade.unpublish(userName, caseName, token);
		} catch (Exception e) {
			throw new CESMSOAPFault(e);
		}
	}

	@WebMethod
	@WebResult(name = "publishStatus")
	public String getPublishStatus(
			@WebParam(name = "userName") String userName,
			@WebParam(name = "caseName") String caseName,
			@WebParam(name = "token") String token) throws CESMSOAPFault {
		try {
			return serviceFacade.getPublishStatus(userName, caseName, token);
		} catch (Exception e) {
			throw new CESMSOAPFault(e);
		}
	}
}